﻿using System.Diagnostics;

namespace HDM.NexusEngine.Core
{
    public class AnimatedSprite : Sprite
    {
        private int _frameX;
        private int _frameY;
        private int _currentFrame;
        private double _currentFrameTime = 0.03;

        /// <summary>
        /// Seconds per frame.
        /// </summary>
        public double Speed { get; set; }

        /// <summary>
        /// Determines if the animation should loop.
        /// </summary>
        public bool Looping { get; set; }

        /// <summary>
        /// Shows that the animation has ended.
        /// </summary>
        public bool Finished { get; set; }

        public bool Pause { get; set; }

        public AnimatedSprite()
        {
            Looping = false;
            Finished = false;
            Pause = true;
            Speed = 0.03; // 30 fps-ish
            _currentFrameTime = Speed;
        }

        /// <summary>
        /// Responsible for updating the current frame and making animation appear to animate.
        /// </summary>
        /// <param name="elapsedTime"></param>
        public void Update(double elapsedTime)
        {
            if (Pause) return;

            if (_currentFrame == GetFrameCount() - 1 && Looping == false)
            {
                Finished = true;
                return;
            }

            _currentFrameTime -= elapsedTime;

            if (_currentFrameTime < 0)
            {
                AdvanceFrame();
                _currentFrameTime = Speed;
                UpdateUVs();
            }
        }

        /// <summary>
        /// Returns the X,Y coordinate of the index position.
        /// </summary>
        /// <param name="frame"></param>
        /// <returns></returns>
        public System.Drawing.Point GetIndexFromFrame(int frame)
        {
            var point = new System.Drawing.Point();
            point.Y = frame/_frameX;
            point.X = frame - (point.Y*_frameY);
            return point;
        }

        /// <summary>
        /// Sets the number of frames along the X and Y of the texture map.
        /// </summary>
        /// <param name="frameX"></param>
        /// <param name="frameY"></param>
        public void SetAnimation(int frameX, int frameY)
        {
            _frameX = frameX;
            _frameY = frameY;
            _currentFrame = 0; // frame no changed so needs reseting.
            UpdateUVs();
        }

        /// <summary>
        /// Moves the animation to the next frame.
        /// </summary>
        public void AdvanceFrame()
        {
            var numberOfFrames = GetFrameCount();
            _currentFrame = (_currentFrame + 1)%numberOfFrames;
        }

        public int GetCurrentFrame()
        {
            return _currentFrame;
        }

        public void SetFrame(int frameIndex)
        {
            Debug.Assert(frameIndex < GetFrameCount());
            _currentFrame = frameIndex;
            UpdateUVs();
        }

        /// <summary>
        /// Gets the total number of frames in the animation.
        /// </summary>
        /// <returns></returns>
        private int GetFrameCount()
        {
            return _frameX*_frameY;
        }

        /// <summary>
        /// Uses the current frame index to change the U,Vs so the sprite correctly represents that frame.
        /// </summary>
        private void UpdateUVs()
        {
            var index = GetIndexFromFrame(_currentFrame);
            var frameWidth = 1.0f/_frameX;
            var frameHeight = 1.0f/_frameY;
            SetUVs(new Point(index.X*frameWidth, index.Y*frameHeight),
                new Point((index.X+1)*frameWidth, (index.Y+1)*frameHeight));
        }
    }
}